#!/usr/bin/env perl
use strict;
use warnings;

use constant OUTDIR => 'drupal-update/';
use constant WEBROOT => 'http://scarab-drupal.googlecode.com/svn-history/';
use constant FILEUPLOAD_URL => 'https://scarab-drupal.googlecode.com/files';

use FindBin;
use File::Temp qw/tempdir/;
use Digest::MD5;
use Getopt::Long;
use Devel::Dwarn;

chdir $FindBin::Bin;
die usage("bad options") unless GetOptions(\my %opt, 'user=s', 'pass=s', 'force', 'noupload');
die usage("no dir specified or dir is not part of an svn checkout") unless @ARGV && -d $FindBin::Bin.'/'.$ARGV[0].'/.svn';
die usage("upload needs user and pass") if !$opt{noupload} && (!$opt{user} || !$opt{pass});

if (!$opt{noupload}) {
	require MIME::Base64;
	MIME::Base64->import();

	for my $mod (qw(LWP::UserAgent IO::Socket::SSL)) {
		my $r = eval "require $mod";
		if (!$r) {
			die "please install $mod (cpan -i $mod) if you want to upload files to googlecode\n";
		}
	}
}

for my $util (qw(svn tar)) {
	my $r = system("$util --version >/dev/null 2>&1");
	if ($r) {
		die "required util $util is not in PATH (or not installed)\n";
	}
}

my $proj_dir = $FindBin::Bin.'/'.shift;

my ($module_name, $targz_path, $need_xml_for_version) = create_release($proj_dir);

if (!$opt{noupload}) {
	upload($targz_path);

	if ($need_xml_for_version) {
		if (! -d OUTDIR.$module_name) {
			mkdir OUTDIR.$module_name or die "cannot mkdir $module_name in ".OUTDIR.": $!\n";
		}

		my $release_xml = OUTDIR.$module_name.'/'.$need_xml_for_version.'.x';

		my @releases = get_releases(OUTDIR, $module_name, $need_xml_for_version);

		write_xml($release_xml, $need_xml_for_version, $module_name, @releases);
	}
}

system qw(svn st);
print "\n";
print "Use this command to commit:\n";
print "\$ svn ci -m'released $targz_path'\n";

exit;

sub upload {
	my ($targz) = @_;
	my ($module, $version) = $targz =~ m{.*/([^-]*)-(\d.x-\d+\.\d+)};
	my $ua = LWP::UserAgent->new;

	(my $basename = $targz) =~ s{.*/}{};

	my %up = (
		summary => "$module $version",
		file => [ $targz ],
	);
	my $uri = URI->new(FILEUPLOAD_URL);

	my $r = $ua->post(FILEUPLOAD_URL, 
		Content_Type => 'form-data',
		Authorization => 'Basic '.encode_base64(join ':', $opt{user}, $opt{pass}),
		Content => \%up,
	);
	if (!$r->is_success) {
		my $reason = $r->status_line;
		if ($r->as_string =~ m/File exists/) {
			$reason = 'File exists: '.$basename;
			print "not uploading: ", $reason, "\n";
			print "remove file or increase version\n\n";
			return;
		}
		elsif ($r->as_string =~ m/Please sign in to upload a file/) {
			$reason = 'Bad username/password';
		}
		die 'Failed to upload file: ', $reason, "\n";
	}
	print "file uploaded\n\n";
}

sub write_xml {
	my ($fn, $drupal_version, $mname, @rls) = @_;
	open my $fh, '>', $fn or die "cannot open release xml $fn for output: $!\n";

	print $fh qq{<?xml version="1.0" encoding="utf-8"?>
<project xmlns:dc="http://purl.org/dc/elements/1.1/">
	<title>Scarab Research recommendation system module</title>
	<short_name>$mname</short_name>
	<dc:creator>NWS</dc:creator>
	<api_version>$drupal_version.x</api_version>
	<project_status>published</project_status>
	<link>https://code.google.com/p/scarab-drupal/</link>
	<terms>
		<term><name>Projects</name><value>Modules</value></term>
		<term><name>Development status</name><value>Under active development</value></term>
		<term><name>Maintenance status</name><value>Actively maintained</value></term>
	</terms>
	<releases>
};
	for my $r (reverse sort { $a->{major} <=> $b->{major} || $a->{minor} <=> $b->{minor} } @rls) {

		(my $url = $r->{targz}) =~ s{.*/}{};
		print $fh
qq"		<release>
			<name>$mname $drupal_version.x-@{[$r->{version}]}</name>
			<version>$drupal_version.x-@{[$r->{version}]}</version>
			<tag>DRUPAL-$drupal_version--@{[$r->{major}]}-@{[$r->{minor}]}</tag>
			<version_major>@{[$r->{major}]}</version_major>
			<version_patch>@{[$r->{minor}]}</version_patch>
			<status>published</status>
			<release_link>https://code.google.com/p/scarab-drupal/</release_link>
			<download_link>@{[FILEUPLOAD_URL.'/'.$url]}</download_link>
			<date>@{[$r->{created}]}</date>
			<mdhash>@{[$r->{md5}]}</mdhash>
			<filesize>@{[$r->{fsize}]}</filesize>
			<terms></terms>
		</release>
";
	}

	print $fh qq{	</releases>\n</project>\n};
}

sub get_releases {
	my ($dir, $module, $drupal_version) = @_;
	my @files = glob("$dir$module-$drupal_version.x*.tar.gz");

	my @releases;
	for my $r (@files) {
		my ($v) = $r =~ m/$drupal_version\.x-(\d+\.\d+)\.tar.gz/;
		my ($major, $minor) = split /\./, $v;
		my @stat = stat $r;

		my $fsize = $stat[7];
		my $ctime = $stat[10];
		
		open my $fh, '<', $r or die "cannot open $r for md5summing: $!\n";
		my $md5 = Digest::MD5->new;
		$md5->addfile($fh);

		push @releases, {
			version => $v,
			major => $major,
			minor => $minor,
			targz => $r,
			created => $ctime,
			fsize => $fsize,
			md5 => $md5->hexdigest,
		};
	}

	return @releases;
}

sub create_release {
	my ($proj_dir) = @_;

	my @info_files = glob("$proj_dir/*.info");
	if (!@info_files) {
		die "$0: cannot find *.info file in $proj_dir\n";
	}
	elsif (@info_files > 1) {
		die "$0: more than one *.info file found in $proj_dir, don't know what to do\n";
	}

	s{.*/}{}, s/\.info$// for (my $module_name = $info_files[0]);

	my ($version_string) = map { /=\s*"?([^"]*)/; $1 } 
		grep { /^\s*version\s*=/i } 
		do { open my $fh, '<', $info_files[0] or die "cannot open *.info file: $!\n"; <$fh> };

	my ($drupal_base, $proj_version) = split /-/, $version_string;

	my $temp_dir = tempdir(CLEANUP => 1);

	my ($out_module_dir) = $proj_dir =~ m{([^/]*)$};
	$out_module_dir =~ tr/-/_/;
	$out_module_dir = 'scarab_'.$out_module_dir;

	my $out_dir = $temp_dir.'/'.$out_module_dir;

	my $targz_name = $module_name.'-'.$version_string.'.tar.gz';
	my $targz_path = OUTDIR.$targz_name;
	
	die "will not overwrite already existing tar.gz: $targz_path\n" if -f $targz_path && !$opt{force};

	system('svn', 'export', $proj_dir, $out_dir);
	system('tar', '-C', $temp_dir, '-czf', $targz_path, $out_module_dir);

	return ($module_name, $targz_path, $drupal_base =~ m/^([67])/);
}

sub usage {
	my $s = shift;
qq{$s
$0 [--force] <--noupload | --user=... --pass=...> <svn dir of drupal module to release>
    user: Your Google account name.
    pass: The googlecode.com password for your account.
          Note that this is NOT your global Google Account password!
    force: force overwrite of same version of tar.gz (in the svn repo)
    svn dir: eg: trunk/drupal6-ubercart
};
}
